﻿/*
 * Created by SharpDevelop.
 * User: ratul
 * Date: 3/9/2010
 * Time: 2:35 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections;
using System.Collections.Generic;
using H264Impl.NET.NAL;
using H264Impl.NET.Coding;

namespace H264Impl.NET.Decoder
{
	/// <summary>
	/// Description of DecodeManager.
	/// </summary>
	public class DecoderMgr
	{
		public DecoderMgr()
		{
		}
		
		public void processNALUnit(byte[] NALUnitStream)
		{
			BitStream _NALBitStream = new BitStream(NALUnitStream);
			NALUnit _NALUnit = createNALUnit(_NALBitStream);
			
			if(_NALUnit.getNALUnitType() == NALUnit.NALUnitType.SEQUENCE_PARAMETER_SET)
				parseSequenceParameterRBSP(_NALUnit.getRBSP());
			else if(_NALUnit.getNALUnitType() == NALUnit.NALUnitType.SEQUENCE_PARAMETER_SET_EXTENSION)
				parseSequenceParameterExtensionRBSP(_NALUnit.getRBSP());
			else if(_NALUnit.getNALUnitType() == NALUnit.NALUnitType.PICTURE_PARAMETER_SET)
				parsePictureParameterRBSP(_NALUnit.getRBSP());
			else if(_NALUnit.getNALUnitType() == NALUnit.NALUnitType.CODED_SLICE_OF_NON_IDR_PICTURE || _NALUnit.getNALUnitType() == NALUnit.NALUnitType.CODED_SLICE_OF_IDR_PICTURE || _NALUnit.getNALUnitType() == NALUnit.NALUnitType.CODED_SLICE_OF_AUXILIARY_CODED_PICTUREWITHOUT_PARTITIONING)
				parseSliceLayerWithoutPartitioningRBSP(_NALUnit, _NALUnit.getRBSP());
			
		}
		
		void parseSliceLayerWithoutPartitioningRBSP(NALUnit _NALUnit, BitStream RBSP)
		{
			activeVideoSequence.parseSliceLayerWithoutPartitioningRBSP(_NALUnit, RBSP);
		}
			
		VideoSequence activeVideoSequence;
		
		void parseSequenceParameterExtensionRBSP(BitStream RBSP)
		{
			UInt32 seqParameterSetId = ExpGolomb.getUnsignedIntegerElement(RBSP);
			
			VideoSequence foundVideoSequence;
			if(!(videoSequenceDictionary.TryGetValue((byte)(seqParameterSetId), out foundVideoSequence)))
			{
				foundVideoSequence = new VideoSequence(seqParameterSetId);
				videoSequenceDictionary.Add((byte)(seqParameterSetId), foundVideoSequence);
			}
			foundVideoSequence.parseSequenceParameterExtensionRBSP(RBSP);
			
		}
		
		void parsePictureParameterRBSP(BitStream RBSP)
		{
			UInt32 picParameterSetId = ExpGolomb.getUnsignedIntegerElement(RBSP);
			UInt32 seqParameterSetId = ExpGolomb.getUnsignedIntegerElement(RBSP);
			
			VideoSequence foundVideoSequence;
			if(!(videoSequenceDictionary.TryGetValue((byte)(seqParameterSetId), out foundVideoSequence)))
			{
				foundVideoSequence = new VideoSequence(seqParameterSetId);
				videoSequenceDictionary.Add((byte)(seqParameterSetId), foundVideoSequence);
			}
			foundVideoSequence.parsePictureParameterRBSP(RBSP, picParameterSetId);
			
		}
		
		
		void parseSequenceParameterRBSP(BitStream RBSP)
		{
			byte profileIDC = (Byte)(RBSP.getBits(8));
			bool constraintSet0Flag = RBSP.getBit();
			bool constraintSet1Flag = RBSP.getBit();
			bool constraintSet2Flag = RBSP.getBit();
			bool constraintSet3Flag = RBSP.getBit();
			bool constraintSet4Flag = RBSP.getBit();
			UInt32 reservedZero3Bits = RBSP.getBits(3);
			
			byte levelIDC = (Byte)(RBSP.getBits(8));
			UInt32 SeqParameterSetId = ExpGolomb.getUnsignedIntegerElement(RBSP);
			
			VideoSequence foundVideoSequence;
			if(videoSequenceDictionary.TryGetValue((byte)(SeqParameterSetId), out foundVideoSequence))
			{
				//duplicate packet ... discard
				return;
			}
			else
			{
				foundVideoSequence = new VideoSequence(SeqParameterSetId);
				videoSequenceDictionary.Add((byte)(SeqParameterSetId), foundVideoSequence);
			}
			foundVideoSequence.parseSequenceParameterRBSP(RBSP, profileIDC, constraintSet0Flag, constraintSet1Flag, constraintSet2Flag, constraintSet3Flag, constraintSet4Flag, reservedZero3Bits, levelIDC);
		
			activeVideoSequence = foundVideoSequence;	
		}
		
	
		static NALUnit createNALUnit(BitStream _NALUnitBitStream)
		{
			bool forbiddenZeroBit = _NALUnitBitStream.getBit();
			UInt32 _NALRefID = _NALUnitBitStream.getBits(2);
			UInt32 nalUnitType =  _NALUnitBitStream.getBits(5);
			
			return new NALUnit(_NALUnitBitStream, forbiddenZeroBit, _NALRefID, nalUnitType);
		}
		
		Dictionary<byte, VideoSequence>  videoSequenceDictionary = new Dictionary<byte, VideoSequence>();
		
	}
}
